// --- Copyright (c) 2006-2008 Stefan Kyntchev ---
// This software is written and copyrighted by Stefan Kyntchev 
// and BeyondPod Team members. All rights are reserved.
// Author contact: support@beyondpod.mobi
// ------------------------------------------------------------------------
// This file is part of BeyondPod RSS Feed Reader and Podcast manager
// (www.codeplex.com/BeyondPod) 
// BeyondPod is free software: you can redistribute it and/or modify 
// it under the terms of the GNU General Public License as published by 
// the Free Software Foundation, either version 3 of the License, or 
// (at your option) any later version. 
//  
// BeyondPod is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
// GNU General Public License for more details. 
//  
// You should have received a copy of the GNU General Public License
// along with BeyondPod. If not, see <http://www.gnu.org/licenses/>
// ------------------------------------------------------------------------
// COPYRIGHT NOTICE: This copyright notice may NOT be removed, obscured or modified 
// without written consent from the author.
// --- End copyright notice --- 


using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using svs.Mobile.BeyondMedia.FeedCore;
using svs.Mobile.BeyondMedia.FeedCore.Helpers;
using svs.Mobile.BeyondMedia.FeedCore.RSS;
using svs.Mobile.BeyondMedia.FeedCore.RSS.Entities;
using svs.Mobile.BeyondMedia.FeedCore.RSS.Templates;
using svs.Mobile.BeyondMedia.Objects;
using svs.Mobile.BeyondMedia.Properties;
using svs.Mobile.BeyondMedia.UiCore;
using svs.Mobile.BeyondMedia.UiCore.GDI;

namespace svs.Mobile.BeyondMedia.Controls
{
	public class RSSFeedContentView : WebBrowser, IView
	{
		private static readonly Uri m_BlankDocument = new Uri("about:blank");

		private static readonly Uri m_DefaultTTSDocument =
			new Uri(Path.Combine(Configuration.RssCachePath, "BeyondPodTTSFeed.txt"));

		private static readonly Uri m_DefaultDocument =
			new Uri(Path.Combine(Configuration.RssCachePath, "BeyondPodFeed." + Configuration.DocumentFileExtension));

		private static readonly Uri m_InfoDocument =
			new Uri(Path.Combine(Configuration.RssCachePath, "BeyondPodInfo." + Configuration.DocumentFileExtension));

		private static Uri m_CurrentDocument;
		private readonly RSSFeedCatcher m_Catcher = new RSSFeedCatcher();
		private readonly Uri[] m_StartingPoints;
		private Feed m_CurrentFeed;
		private bool m_RefreshOnVisible;
		public event EventHandler BackClick;
		public event Helpers.NavigationEvent Navigation;

		#region Constructors

		public RSSFeedContentView()
		{
			m_Catcher.FeedDownloaded += OnFeedDownloaded;
			m_Catcher.FeedUpdateError += OnFeedUpdateError;
			m_Catcher.FeedUpdateProgress += OnFeedUpdateProgress;
			m_Catcher.FeedUpdateStarted += OnFeedUpdateStarted;
			m_Catcher.FeedUpdateTerminated += OnFeedUpdateTerminated;
			BrowserCtrl.Navigating += OnNavigating;
			BrowserCtrl.Navigated += OnNavigated;
			BrowserCtrl.DocumentCompleted += OnDocumentCompleted;

			m_StartingPoints = new Uri[7];

			m_StartingPoints[0] = new Uri(Path.Combine(Configuration.RssCachePath, "BeyondPodFeed"));
			m_StartingPoints[1] = Configuration.HelpFilePath;
			m_StartingPoints[2] = new Uri(Configuration.FeedUpdateLogFilePath);
			m_StartingPoints[3] = Configuration.VersionCheckURL;
			m_StartingPoints[4] = m_InfoDocument;
			m_StartingPoints[5] = Configuration.SuggestFeatureURL;
			m_StartingPoints[6] = Configuration.DonateURL;
		}

		#endregion

		#region Public Properties

		public Feed CurrentFeed
		{
			get { return m_CurrentFeed; }
			set
			{
				if (m_CurrentFeed != value || CurrentRssFeed == null || (m_CurrentFeed != null && m_CurrentFeed.HasNewContent))
				{
					m_CurrentFeed = value;
					ViewManager.RefreshTitle();
					m_CurrentFeed.HideReadItems = Configuration.HideReadItems; //Reset to the global default

					try
					{
						if (m_CurrentFeed != null)
						{
							if (m_CurrentFeed.HasUrl)
							{
								CurrentRssFeed = null;

								if (Configuration.UpdateOnFeedOpen && !m_CurrentFeed.UpdateFailed) //if the previous update failed - just open the cached version
									UpdateFeedContent(true);
								else
									OnNewFeedAvailable(m_CurrentFeed);
							}
							else
							{
								Helpers.Beep();
							}
						}
					}
					catch (Exception)
					{
						MessageBoxEx.Show(Resources.UnableToOpenFeedUrl,
						                  Resources.UnableToOpenFeedUrlTitle, MessageBoxButtons.OK, MessageBoxIcon.Exclamation,
						                  MessageBoxDefaultButton.Button1);
					}
				}
				else
				{
					if (m_RefreshOnVisible)
					{
						BrowserCtrl.Refresh();
					}
				}

				m_RefreshOnVisible = false;
				CustomizeMenus();
			}
		}

		/// <summary>
		/// True if the browser control is currently showing a URL that is not one of our default Urls
		/// </summary>
		public bool IsShowingWebPage
		{
			get
			{
				if (BrowserCtrl.Url == null || BrowserCtrl.Url.AbsolutePath.Length == 0)
					return false;

				if (BrowserCtrl.Url.Equals(m_BlankDocument))
					return false;

				foreach (Uri uri in m_StartingPoints)
				{
					//ifnore any parameters in the url
					if (uri != null && BrowserCtrl.Url.AbsolutePath.StartsWith(uri.AbsolutePath))
						return false;
				}

				return true;
			}
		}

		/// <summary>
		/// True if a feed is currently updating
		/// </summary>
		public bool IsUpdating
		{
			get { return m_Catcher.IsUpdating; }
		}

		/// <summary>
		/// True if the browser is currently loading content
		/// </summary>
		public bool IsBrowserBusy
		{
			get { return BrowserCtrl.IsBusy; }
		}

		/// <summary>
		/// Gets the title.
		/// </summary>
		/// <value>The title.</value>
		public string Title
		{
			get { return m_CurrentFeed == null ? "Feed Content" : (m_CurrentFeed.HasUnreadItems ? ":" : string.Empty) + m_CurrentFeed.Name + (CurrentRssFeed != null? " (" + CurrentRssFeed.Items.Count + ")": string.Empty); }
		}

		/// <summary>
		/// Gets the view menu.
		/// </summary>
		/// <value>The view menu.</value>
		public MainMenu ViewMenu
		{
			get { return MenuManager.GetMenu(Helpers.ViewNames.FeedContent); }
		}

		public RssFeed CurrentRssFeed { get; private set; }

		#endregion

		#region Public Methods
		public void OnBeforeActivate()
		{
		}

		/// <summary>
		/// Gets a screenshot of the view for transitions
		/// </summary>
		/// <returns></returns>
		public Bitmap CreateScreenShot()
		{
			return Gdi.CreateControlSnapshot(this);
		}

		public void OnAfterDeactivate() {}

		public void OnAfterActivate()
		{
			BrowserCtrl.Focus();
		}

		public void SetURL(Uri aUri, bool showInfo)
		{
			if (InvokeRequired)
			{
				SetUrlDelegate cbDel = SetURL;
				Invoke(cbDel, aUri, showInfo);
				return;
			}

			if (aUri.Equals(m_DefaultDocument))
			  if (!ConstructDefaultDocument()) // generate the document 
				  return;

			if (BrowserCtrl.IsBusy)
			{
			  //If the browser is loading another document we have to waiting on OnDocumentCompleted event.
			  m_CurrentDocument = aUri;
			}
			else
			{
				if (showInfo)
					SetDocumentTextTo("Loading ...");
				SetURLInternal(aUri);
			}

			if (BrowserCtrl.Visible)
				BrowserCtrl.Focus();
		}

		private void SetDocumentTextTo(string text)
		{
			SetDocumentText(string.Format("<h3 align='center' style='color: {0};'>{1}</h3>", CoreHelper.SYS_COLOR_A, text));
		}

		public void SetExternalURL(Uri aUri, string title)
		{
			if (InvokeRequired)
			{
				SetExternalUrlDelegate cbDel = SetExternalURL;
				Invoke(cbDel, aUri, title);
				return;
			}

			m_CurrentFeed = null;
			CurrentRssFeed = null;

			ViewManager.TrySetTitle(title);

			SetURL(aUri, true);
		}

		public void NavigateBack()
		{
			if (IsDNSUrl(BrowserCtrl.Url) || IsShowingWebPage)
			{
				BrowserCtrl.GoBack();
			}
			else
			{
				Helpers.Beep();
			}
		}

		public void BrowserStop()
		{
			BrowserCtrl.Stop();
		}

		public void BrowserRefresh()
		{
			//If we are showing a feed content, rebuild it and reload
			if (m_CurrentFeed != null && BrowserCtrl.Url != null && BrowserCtrl.Url.AbsolutePath.StartsWith(m_StartingPoints[0].AbsolutePath))
			{

#if DEBUG
				RssTemplatingEngine.ReloadAlltemplates(); //-- FORCE RELOAD FOR DEBUG PURPOSES
				Helpers.Beep();
#endif

				CurrentRssFeed = null;
				OnNewFeedAvailable(m_CurrentFeed);
			}
			else
				BrowserCtrl.Refresh();
		}

		public void UpdateFeedContent(bool showInfoMsg)
		{
			if (m_Catcher != null)
			{
				if (m_Catcher.IsUpdating)
				{
					MessageBoxEx.Show(Resources.AnotherDownloadInProgressMsg, Resources.AnotherDownloadInProgressTitle,
					                  MessageBoxButtons.OK,
					                  MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
					return;
				}

				if (!CoreHelper.IsDeviceConnected())
				{
					MessageBoxEx.Show(Resources.DeviceNotConnectedMsg, Resources.DeviceNotConnectedTitle,
					                  MessageBoxButtons.OK,
					                  MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
					return;
				}

				if (showInfoMsg) 
				{
					if (!m_CurrentFeed.IsAggregate)  // Aggrgate feeds are generated too fast for the InfoMessage to show up
					{
						RssTemplatingEngine.TransformNoCachedFeedInfo(CurrentFeed.Name, m_InfoDocument.LocalPath);
						SetURL(m_InfoDocument, false);	
					}
				}

				ViewManager.SetCustomTitle(m_CurrentFeed.IsAggregate?"Generating...":"Updating...", true);
				m_Catcher.InitializeCatcher(m_CurrentFeed);
				m_Catcher.UpdateFeed(true);
			}
		}

		public void StopFeedUpdate()
		{
			m_Catcher.Stop();
		}

		public void UpdateDownloadStaus(Feed feed, Track track)
		{
			if (CurrentFeed != feed)
				return;

			//If the Browser view was never opened
			if (CurrentRssFeed == null)
				return;

			if (ConstructDefaultDocument())
				RefreshOnVisible();
		}

		public void RefreshOnVisible()
		{
			if (Visible)
				BrowserCtrl.Refresh();
			else
				m_RefreshOnVisible = true; //refresh next time the browser becomes visible
		}

		public void GoBackToContent()
		{
			SetURL(m_DefaultDocument, false);
		}

		#endregion

		#region Private Methods

		private WebBrowser BrowserCtrl
		{
			get { return this; }
		}

		protected override void OnKeyDown(KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
					//Left Right
				case Keys.Left:
					OnNavigation(Helpers.NavigationDirection.Back);
					e.Handled = true;
					break;

					//Left Right
				case Keys.Right:
					OnNavigation(Helpers.NavigationDirection.Forward);
					e.Handled = true;
					break;
			}

			if (!e.Handled)
			{
				base.OnKeyDown(e);

				if (!e.Handled)
					e.Handled = ShortcutKeyManager.HandleKey(e.KeyCode);
			}
		}

		protected override void OnKeyPress(KeyPressEventArgs e)
		{
			if (e.KeyChar == (Char) Keys.Escape)
			{
				e.Handled = true;
				if (BackClick != null)
					BackClick(this, null);
			}
		}

		#region Feed update event handlers

		private void OnFeedUpdateTerminated(Feed aFeed)
		{
			if (InvokeRequired)
			{
				UpdateDelegate cbDel = OnFeedUpdateTerminated;

				Invoke(cbDel, aFeed);
				return;
			}

			ViewManager.SetCustomTitle("Canceled!", false);
			CustomizeMenus();
		}

		private void OnFeedUpdateStarted(Feed aFeed)
		{
			if (InvokeRequired)
			{
				UpdateDelegate cbDel = OnFeedUpdateStarted;

				Invoke(cbDel, aFeed);
				return;
			}

			ViewManager.SetCustomTitle("Connecting...", true);
			CustomizeMenus();
		}

		private void OnFeedUpdateProgress(Feed aFeed)
		{
			if (InvokeRequired)
			{
				UpdateDelegate cbDel = OnFeedUpdateProgress;

				Invoke(cbDel, aFeed);
				return;
			}

			if (aFeed.UpdateSize <= 0)
				ViewManager.SetCustomTitle(string.Format("Updated {0}", CoreHelper.GetFileLengthAsString(aFeed.UpdatedPortion)), true);
			else
			{
				ViewManager.SetCustomTitle(string.Format(" {0}% of {1}", aFeed.UpdatePercent, CoreHelper.GetFileLengthAsString(aFeed.UpdateSize)), true);
			}
		}

		private void OnFeedUpdateError(Feed aFeed, Exception ex)
		{
			if (InvokeRequired)
			{
				UpdateErrorDelegate cbDel = OnFeedUpdateError;

				Invoke(cbDel, aFeed, ex);
				return;
			}

			ViewManager.SetCustomTitle("Failed!", false);
			CurrentFeed.UpdateFailed = true;
			SetDocumentText(RssTemplatingEngine.FormatFeedLoadError(aFeed.Name, "Feed update failed!<br/>" + ex.Message));
			CustomizeMenus();
		}

		private void OnFeedDownloaded(Feed aFeed)
		{
			ViewManager.SetCustomTitle("Loading...", false);
			OnNewFeedAvailable(aFeed);
		}

		#endregion

		private void OnNewFeedAvailable(Feed aFeed)
		{
			if (InvokeRequired)
			{
				UpdateDelegate cbDel = OnNewFeedAvailable;

				Invoke(cbDel, aFeed);
				return;
			}

			CustomizeMenus();

			try
			{
				Cursor.Current = Cursors.WaitCursor;

				bool feedUpdated = false;
				if (CurrentRssFeed == null || CurrentFeed.HasNewContent)
				{
					ViewManager.TrySetTitle("Loading...");

					//Reload it from the local cache
					CurrentRssFeed = RssFeedCache.LoadFromCache(CurrentFeed);

					if (CurrentRssFeed != null)
					{
						ItemReadHistory.PurgeOldRecordsForFeed(CurrentRssFeed);
						CurrentRssFeed.SyncOwnerFeedReadStatus(); //sync the feed read status
					}

					feedUpdated = true;
				}

				if (CurrentRssFeed == null) // Local cache is not availalbe
				{
					UpdateFeedContent(true);
				}
				else
				{
					Application.DoEvents();
					if (feedUpdated)
					{
						//If the feed has its default name - update it
						m_CurrentFeed.UpdateNameIfDefault(CurrentRssFeed.Title);

						SetURL(m_DefaultDocument, false);
						ViewManager.Feeds.NotifyFeedStatusChanged(m_CurrentFeed);
					}
					else
					{
						ViewManager.SetCustomTitle("Feed Unchanged!", false);
						Application.DoEvents();
						Thread.Sleep(700);
					}
				}
			}
			catch (CoreHelper.EncounteredOpmlFeedException)
			{
				Cursor.Current = Cursors.Default;
				CurrentFeed.Type = FeedType.OPMLFeed;
				CommandManager.CmdImportFeeds(CurrentFeed);
			}
			catch (Exception ex)
			{
				CurrentRssFeed = null;
				if (RssTemplatingEngine.TransformFeedLoadError(CurrentFeed.Name, ex.Message, m_InfoDocument.LocalPath))
					SetURL(m_InfoDocument, false);
				else
					SetDocumentText(RssTemplatingEngine.FormatFeedLoadError(CurrentFeed.Name, ex.Message));
			}
			finally
			{
				Cursor.Current = Cursors.Default;
				//Clear the new flag
				CurrentFeed.HasNewContent = false;
				ViewManager.RefreshTitle();
			}
		}

		private void OnNavigation(Helpers.NavigationDirection i_direction)
		{
			if (Navigation != null)
				Navigation(this, i_direction);
		}

		private void OnDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
		{
		if (m_CurrentDocument != null)
			{
			  // delayed loading because the browser was busy
				Uri u = m_CurrentDocument;
				m_CurrentDocument = null; // prevent recursive calls if 'SetURLInternal(u)' returns immediately with 'OnDocumentCompleted()'
				SetURLInternal(u);
			}

			ViewManager.TrySetTitle(Title);
			CustomizeMenus();
		}

		private void OnNavigated(object sender, WebBrowserNavigatedEventArgs e)
		{
			CustomizeMenus();
		}

		private void OnNavigating(object sender, WebBrowserNavigatingEventArgs e)
		{
			const string ToggleStatusPrefix = "BeyondPodToggleReadStatusFor";

			if (CurrentRssFeed == null)
			{
				if (e.Url.ToString().EndsWith("BeyondPodDownloadUpdate")) // Download Update is clicked
				{
					e.Cancel = true;
					CommandManager.CmdDownloadApplicationUpdate(e.Url);
				}
				else if (e.Url.ToString().EndsWith("BeyondPodUpdateFeed"))
				{
					e.Cancel = true;
					UpdateFeedContent (false);
				}
				else if (e.Url.ToString().EndsWith("BeyondPodStopUpdate"))
				{
					e.Cancel = true;
					StopFeedUpdate();
				}

				return;
			}

			if (e.Url.Scheme == Uri.UriSchemeFile && !e.Url.AbsolutePath.StartsWith(m_StartingPoints[0].AbsolutePath))
			{
				e.Cancel = true;

				//Download all link was clicked
				if (e.Url.ToString().EndsWith("BeyondPodDownloadAllPodcasts"))
				{
					var toDownload = new List<KeyValuePair<Feed, RssEnclosure>>();
					var enclosures = CurrentRssFeed.GetDownloadableEnclosures();

					if (CurrentFeed != null && enclosures.Count > 0)
					{
						foreach (RssEnclosure enclosure in enclosures)
						{
							toDownload.Add(new KeyValuePair<Feed, RssEnclosure>(CurrentFeed, enclosure));
						}

						DownloadEnclosures(toDownload);
					}
					else
					{
						Helpers.Beep();
					}
				}
					//Show Podcasts
				else if (e.Url.ToString().EndsWith("BeyondPodShowAllPodcasts"))
				{
					CommandManager.CmdToggleFeedView(CurrentFeed, FeedViewType.Tracks);
				}
				else if (e.Url.ToString().EndsWith("BeyondPodShowNextFeed"))
				{
					CommandManager.CmdShowNextFeedContent(1, false);
				}
				else if (e.Url.ToString().EndsWith("BeyondPodShowPreviousFeed"))
				{
					CommandManager.CmdShowNextFeedContent(-1, false);
				}
				else if (e.Url.ToString().EndsWith("BeyondPodUpdateFeed"))
				{
					CommandManager.CmdUpdateFeed();
				}
				else if (e.Url.ToString().EndsWith("BeyondPodShowAllReadItems"))
				{
					CurrentFeed.HideReadItems = false;  //disable the hiding of read items. It will be re-set to the global setting automatically during the refresh
					BrowserRefresh();
				}
				else if (e.Url.ToString().IndexOf(ToggleStatusPrefix) != -1)
				{
					int itemID = 0;

					try
					{
						itemID = int.Parse(e.Url.ToString().Substring(e.Url.ToString().IndexOf(ToggleStatusPrefix) + ToggleStatusPrefix.Length));
					}
					catch
					{
						//Try Parse
					}

					if(itemID != 0)
					{
						RssFeedItem item = CurrentRssFeed.GetItemById(itemID);
						if (item != null)
						{
							item.Read = !item.Read;
							ViewManager.RefreshTitle();
						}
					}
				}
				else if (e.Url.ToString().EndsWith("BeyondPodIgnore"))
				{
					e.Cancel = true;
				}
				else if (e.Url.ToString().EndsWith("BeyondPodPage"))
				{
					e.Cancel = false; // next/prev page link
				}

				else if (e.Url.ToString().EndsWith("BeyondPodDeleteTrack"))
				{
					var t = FeedRepository.GetTrackByPath(e.Url.LocalPath.Replace("?BeyondPodDeleteTrack",string.Empty));
					if (t != null)
					{
						CommandManager.CmdDeleteTrack(t);
					}
					else
					{
						Helpers.Beep();
					}
				}
				else // Play link was clicked
				{
					Track t = FeedRepository.GetTrackByPath(e.Url.LocalPath);
					if (t != null)
					{
						CommandManager.PlayTrack(t);
					}
					else
					{
						Helpers.Beep();
					}
				}
			}
				//Download enclosure is clicked
			else if (RssTemplatingEngine.IsDownloadEncodedEnclosureUrl(e.Url.OriginalString))
			{
				e.Cancel = true;

				string decodedUrl = RssTemplatingEngine.DecodeDownloadEnclosureUrl(e.Url.OriginalString);
				RssEnclosure encl = CurrentRssFeed.GetEnclosureByUrl(decodedUrl);

				if (CurrentFeed != null && encl != null)
				{
					//Before checking for the size, make sure we have a valid place to download
					if(!FeedRepository.EnsureFeedHasDownloadPath(CurrentFeed))
						return;

					if (!FlashStorageLocation.CheckIfSpaceIsAvailable(Path.GetDirectoryName(CurrentFeed.FeedPath), encl.Length??0))
					{
						var freeSpace = FlashStorageLocation.GetFreeSpaceAtFeedDownloadPath(CurrentFeed);

						MessageBoxEx.Show(
							string.Format(Resources.InsufficientSpaceToDownloadPodcastMsg, CoreHelper.GetFileLengthAsString(encl.Length),
							              CoreHelper.GetFileLengthAsString(freeSpace),
							              string.IsNullOrEmpty(CurrentFeed.FeedPath)
							              	? Configuration.EnclosureDownloadPath
							              	: CurrentFeed.FeedPath), Resources.InsufficientSpaceToDownloadPodcastTitle);
						return;
					}

					var toDownload = new List<KeyValuePair<Feed, RssEnclosure>>();
					toDownload.Add(new KeyValuePair<Feed, RssEnclosure>(CurrentFeed, encl));
					DownloadEnclosures(toDownload);
				}
			}
			else if (RssTemplatingEngine.IsStreamEncodedEnclosureUrl(e.Url.OriginalString))
			{
				e.Cancel = true;

				string decodedUrl = RssTemplatingEngine.DecodeStreamEnclosureUrl(e.Url.OriginalString);
				RssEnclosure encl = CurrentRssFeed.GetEnclosureByUrl(decodedUrl);
				if (encl == null)
				{
					Helpers.Beep();
					return;
				}

				Uri enclosurePath = CoreHelper.SafeCreateUri(encl.Url);
				if (enclosurePath != null)
				{
					var t = new Track(string.Empty,m_CurrentFeed);
					t.Name = encl.FileName;
					t.Url = encl.Url;
					t.Protocol = enclosurePath.Scheme;
					CommandManager.CmdStreamTrack(t);
				}
				else 
					Helpers.Beep();
			}

			if (e.Cancel != true)
			{
				CustomizeMenus();

				//We are going to an external URL
				if (e.Url != null && (e.Url.Scheme == Uri.UriSchemeHttp || e.Url.Scheme == Uri.UriSchemeHttps))
				{
					e.Cancel = true;
					CommandManager.NavigateToPublicExternalUrl(e.Url,m_CurrentFeed == null? "": m_CurrentFeed.Name);
				}
			}
		}

		private void CustomizeMenus()
		{
			MenuManager.WellKnownMenus[MenuManager.WellKnownMenuItems.FeedContent_GoBack].Text = IsShowingWebPage ? "Go Back" : "Feeds";
		}

		private void DownloadEnclosures(List<KeyValuePair<Feed, RssEnclosure>> toDownload)
		{
			if (!EnclosureDownloadManager.IsDownloading)
			{
				CurrentFeed.NumDownloadedEnclosures = 0;
				CurrentFeed.NumEnclosuresAvailableForDownload = toDownload.Count;
			}
			else
			{
				CurrentFeed.NumEnclosuresAvailableForDownload += toDownload.Count;
			}

			EnclosureDownloadManager.DownloadEnclosures(toDownload);
		}

		private void SetDocumentText(string text)
		{
			BrowserCtrl.DocumentText = text;
		}

		private bool ConstructDefaultDocument()
		{
			if (CurrentRssFeed == null)
				return false;

			RssTemplatingEngine.BuildRssView(m_DefaultDocument.LocalPath, CurrentRssFeed, m_CurrentFeed, 60, CurrentFeed.HideReadItems);
			return true;
		}

		public bool SpeakCurrentDocument()
		{
			if (CurrentRssFeed == null || string.IsNullOrEmpty(Configuration.TTSHandler))
				return false;

			try
			{
				ViewManager.TrySetTitle("Generating...");
				Cursor.Current = Cursors.WaitCursor;
				Application.DoEvents();
				RssTemplatingEngine.BuildTTSView(m_DefaultTTSDocument.LocalPath, CurrentRssFeed, m_CurrentFeed, 600, CurrentFeed.HideReadItems);
				CommandManager.CmdExecuteCommandLine(Configuration.TTSHandler.Replace("%1", "\"\\" + m_DefaultTTSDocument.LocalPath + "\""));
			}
			finally
			{
				Cursor.Current = Cursors.Default;
				ViewManager.TrySetTitle(Title);	
			}

			return true;
		}

		private static bool IsDNSUrl(Uri uri)
		{
			return uri != null &&
			       (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps);
		}

		private void SetURLInternal(Uri url)
		{
			try
			{
				if (BrowserCtrl.Url!=null && BrowserCtrl.Url.Equals(url))
  			  BrowserCtrl.Refresh(); // just refresh the current broser content
				else
  				BrowserCtrl.Url = url;
			}
			catch
			{
				CoreHelper.WriteLogEntry(
					"Failed to set the URL to the Browser Control. Possible .htm extension handler in the registry is invalid!");
			}
		}

		#endregion

		#region Nested type: SetExternalUrlDelegate

		private delegate void SetExternalUrlDelegate(Uri i_uri, string title);

		#endregion

		#region Nested type: SetUrlDelegate

		private delegate void SetUrlDelegate(Uri i_uri, bool showInfoMsg);

		#endregion

		#region Nested type: UpdateDelegate

		private delegate void UpdateDelegate(Feed aFeed);

		#endregion

		#region Nested type: UpdateErrorDelegate

		private delegate void UpdateErrorDelegate(Feed aFeed, Exception ex);

		#endregion
		
	}
}